/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.lite.event;

import java.util.Date;

import com.xsm.lite.util.ToStringBuilder;


/**
 * Represents a request for which there is a response.
 * 
 * @author Sony Mathew
 */
public abstract class RequestEvent extends Event {

    /**
     * Identifies a processor for this event type.
     * 
     * Subclasses should extend and provide their unique signature for processing.
     *      e.g. MyResponseEvent process(MyRequestEvent);
     *      
     * Exceptions (expected ones) should be placed in the response and NOT thrown.
     *       
     */
    public interface Processor {
    }
    
    /**
     * A mediator for request events (usually an EventManger) connects a Processor to the Events they can process.
     * Subclasses should rarely (if ever) need to subclass or use this interface directly.
     */
    public interface Mediator {        
        /**
         * Indicate that you'd like to be the processor for this event type. Only one processor
         *  can be registered for an event type.  An IllegalStateException will result if more than 1 processor
         *      attempts to register for a given request event.
         * 
         * author Sony Mathew
         */    
        public void addProcessor(Class<? extends RequestEvent> eventClass, RequestEvent.Processor p);
        
        /**
         * Remove the specific processor for the specific event.
         * 
         * author Sony Mathew
         */
        public void removeProcessor(Class<? extends RequestEvent> eventClass, RequestEvent.Processor p);
        
        /**
         * Provide the registered processor for given event's type. If a processor is not found for this event type 
         *  an IllegalStateException is expected to be thrown.
         * 
         * author Sony Mathew
         */
        public RequestEvent.Processor getProcessor(RequestEvent e);
    }
            
    /**
     * Obtain a string key that identifies this request event.
     * 
     * author Sony Mathew
     */
    public abstract String getKey();

    
    /**
     * Process this event using the provided mediator (usually an EventManger).
     *  
     * Unlike fire/listen of events which is an asynchronous approach to event handling, this event processing
     *  is a direct synchronous approach to processing an event using a registered processor and obtaining a
     *  response.
     *  
     * Subclasses should override to provide their specific response event 
     *  by obtaining and casting the processor from the mediator.
     *  
     *   e.g. MyResponseEvent process(RequestEvent.Mediator m) {
     *            return ((MyRequestEvent.Processor)m.getProcessor(this)).process(this);
     *        }
     * 
     * To process an event do the following (assuming the Mediator is an EventManager available to you).
     *  e.g. MyResponseEvent myResponseEvent = myRequestEvent.process(eventManger);
     *  
     * author Sony Mathew
     */
    public abstract ResponseEvent process(RequestEvent.Mediator m);


    /**
     * Cannot override, implement getKey() instead.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public final boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o == null || !(o.getClass().equals(this.getClass()))) {
            return false;
        }
        RequestEvent other = (RequestEvent)o;
        return this.getKey().equals(other.getKey());                
    }


    /**
     * Cannot override, implement getKey() instead.
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public final int hashCode() {
        return getKey().hashCode();
    }


    /**
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return getKey();
    }
    
    /**
     * builds a unique key consisting of a String representation of the class
     * plus a timestamp
     * @return
     */
    protected String buildUniqueKey() {
    	return new ToStringBuilder(getClass()).toString() + new Date().getTime();
    }
    
}
